package com.shujia.scala

object Demo13ScalaFunc2 {
  /**
   * 面向对象编程：将对象传来传去 对象是有类型限制的
   * 面向函数编程：将函数传来传去 函数也应该有类型限制
   * 函数的类型是什么？？？
   * 函数的类型由函数的 参数类型（个数、顺序、类型都要一致） 以及 返回值类型 共同决定
   * 函数式编程（面向函数编程、高阶函数）：
   * 1、以函数作为参数
   * 2、以函数作为返回值
   *
   */
  def main(args: Array[String]): Unit = {

    func1()

    // 在main方法中定义函数
    def func1(): Unit = {
      println("hello" + func2())

      def func2() = {
        "world"
      }
    }


    // funX支持传入一个函数
    // 该函数的类型有限制，必须由两个参数都是String类型，而且返回值类型为Int
    funX(f1)
    funX(f2)
    //    funX(fun1) // fun1的类型不符合要求

    /**
     * 匿名函数的简化：
     * 1、如果方法体只有一行 {}可以省略
     * 2、返回值类型不能指定 会自动推断
     * 3、参数类型可以省略
     * 4、如果参数只有一个 括号可以省略
     * 5、如果参数只被使用一次 参数可以省略 用 _ 替代
     */
    funX((str1: String, str2: String) => {
      // 方法体
      str1.toInt + str2.toInt + 3
    })
    // 如果方法体只有一行 {}可以省略
    funX((str1: String, str2: String) => str1.toInt + str2.toInt + 3)

    // 参数类型可以省略
    funX((str1, str2) => str1.toInt + str2.toInt + 3)

    // funX2支持传入一个函数
    // 该函数的类型有限制，参数是String类型，返回值类型为Int
    // 如果参数只有一个 括号可以省略
    funX2(str1 => str1.toInt + 4)

    // 如果参数只被使用一次 参数可以省略 用 _ 替代
    funX2(_.toInt + 4)

    println(f("20", "30"))

    funX(f)
  }

  // 参数为 String类型 返回值类型为Int 的 函数
  def fun1(str: String) = {
    str.toInt + 100
  }

  // 参数为 String类型 返回值类型为Int 的 函数
  def fun2(str: String) = {
    str.toInt + 200
  }

  // 参数为 String类型 返回值类型为String 的 函数
  // fun1 与 fun2 是同一类型的函数  fun3不属于同一类型
  def fun3(str: String) = {
    str + 100
  }

  // 1、以函数作为参数
  // funX 支持传入一个函数f（类型：参数为String,String 返回值为Int） 并进行调用
  def funX(f: (String, String) => Int) = {
    println("开始调用f")
    val i: Int = f("100", "200")
    println(i)
    println("结束调用")
  }

  def f1(str1: String, str2: String): Int = {
    str1.toInt + str2.toInt + 1
  }

  def f2(str1: String, str2: String): Int = {
    str1.toInt + str2.toInt + 2
  }
  // funX2 支持传入一个函数f（类型：参数为String 返回值为Int） 并进行调用
  def funX2(f: String => Int) = {
    val i = f("100")
    println(i)
  }

  /**
   * 匿名函数：实际上是对函数的简写
   * 按照字面理解就是没有名字的函数
   * (str1: String, str2: String) => {
   * // 方法体
   * str1.toInt + str2.toInt + 3
   * }
   * (str1: String, str2: String) 表示匿名函数的参数及类型
   * =>
   * {方法体}  方法体中的最后一行代码作为匿名函数的返回值
   *
   * 匿名函数 实际上为了方便调用 可以拥有名字
   *
   */

  val f = (str1: String, str2: String) => {
    // 方法体
    str1.toInt + str2.toInt + 3
  }
}
